/*******************************************************************************
 * Copyright (c) 2001, 2002 Boris Pruessmann and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v0.5
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors:
 *    Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.core.internal.api;

import java.util.Stack;

import net.sourceforge.perforce.api.IPerforceCommand;
import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.api.IResolveResult;
import org.apache.regexp.RE;
import org.apache.regexp.RECompiler;
import org.apache.regexp.REProgram;
import org.apache.regexp.RESyntaxException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;

/**
  * ClientUser object for the 'p4 resolve' command.
  * 
  * @version $Revision: 252 $
  * @author  <a href="mailto:bpruessmann@users.sourceforge.net">Boris Pruessmann</a>
  */
public class Resolve extends AbstractPerforceCommand implements IPerforceCommand
{
	// --------------------------------------------------------------------------- Private Constants
	
	/** Regular Expression: resolve skipped. */
	private static final REProgram RE_RESOLVE_SKIPPED;
	/** Regular Expression: matching result without revisions */
	private static final REProgram RE_WITHOUT_REVISIONS;
	/** Regular Expression: matches result with one revision */
	private static final REProgram RE_WITH_ONE_REVISION;
	/** Regular Expression: matches the rest. */
	private static final REProgram RE_WITH_BOTH_REVISIONS;	
	
	// -------------------------------------------------------------------- Static Initializer Block
	
	static
	{
		try
		{
			RECompiler compiler = new RECompiler();
			RE_RESOLVE_SKIPPED = compiler.compile("^(//.*) - resolve skipped.$");      //$NON-NLS-1$
			RE_WITHOUT_REVISIONS = compiler.compile("^(.*) - (.*) (//.*)$");           //$NON-NLS-1$
			RE_WITH_ONE_REVISION = compiler.compile("^(.*) - (.*) (//.*)(#([0-9]*))$");//$NON-NLS-1$
			RE_WITH_BOTH_REVISIONS = 
				compiler.compile("^(.*) - (.*) (//.*)(#([0-9]*),#([0-9]*))$");         //$NON-NLS-1$
		}
		catch (RESyntaxException e)
		{
			throw new RuntimeException(e.getMessage());
		}
	}
	
	// --------------------------------------------------------------------------- Private Variables

	/** Stores the operation's result. */	
	private Stack result = new Stack();

	// --------------------------------------------------------------- Public Static Factory Methods
	
	/** Convenience method for creating instances of Resolve. 
	  *
	  * @return the new Reopen instance.
	  */
	public static final Resolve newInstance()
	{
		MultiStatus status = new MultiStatus(PerforceCorePlugin.ID, IStatus.OK, "", null);
		return new Resolve(status);
	}

	// ------------------------------------------------------------------------ Private Constructors

	/** Creates a new instance. 
	  * 
	  * @param status the MultiStatus instance.
	  */
	private Resolve(MultiStatus status)
	{
		super(status);
	}	

	// -------------------------------------------------------- Public Methods (IClientUserDelegate)

	/** @see IClientUser#outputInfo(char, String) */
	public void outputInfo(char level, String infoString)
	{		
		if (level != '0')
		{
			return;
		}
		
		RE re = new RE(RE_RESOLVE_SKIPPED); 
		if (re.match(infoString))
		{
			handleResolveSkipped(re);
			return;
		}		
		
		re = new RE(RE_WITH_BOTH_REVISIONS);
		if (re.match(infoString))
		{
			handleWithRevision(re);
			return;
		}
		
		re = new RE(RE_WITH_ONE_REVISION);
		if (re.match(infoString))
		{
			handleWithRevision(re);
			return;
		}

		re = new RE(RE_WITHOUT_REVISIONS);
		if (re.match(infoString))
		{
			handleWithoutRevisions(re);
			return;
		}
	}

	// ----------------------------------------------------------- Public Methods (IPerforceCommand)

	/** @see IPerforceCommand#getCommand() */
	public String getCommand()
	{
		return "resolve";                                                              //$NON-NLS-1$
	}

	/** @see IPerforceCommand#getStatus() */
	public IStatus getStatus()
	{
		return getMultiStatus();
	}	
		
	// ------------------------------------------------------------------------------ Public Methods
	
	/** Returns the result of the operation.
	  * 
	  * @return array of result objects.
	  */
	public IResolveResult[] getResult()
	{
		return (IResolveResult[]) result.toArray(new IResolveResult[result.size()]);
	}
	
	// ----------------------------------------------------------------------------- Private Methods

	/** Handle infostring matching regular expression RE_RESOLVE_SKIPPED.
	  *
	  * @param re the regular expression. 
	  */
	private void handleResolveSkipped(RE re)
	{
		// resolve skipped comes as addition to previous output.
		Result resolveResult = (Result) result.peek();			
		resolveResult.setIsSkipped(true);
		
	}

	/** Handle infostring matching regular expression RE_WITH_BOTH_REVISIONS and 
	  * RE_WITH_ONE_REVISION.
	  *
	  * @param re the regular expression. 
	  */
	private void handleWithRevision(RE re)
	{
		Result resolveResult = new Result();
		resolveResult.setLocalFilename(re.getParen(1));
		resolveResult.setDepotFilename(re.getParen(3));
		resolveResult.setRevision(new Integer(re.getParen(5)));
		
		if (result.isEmpty())
		{
			result.push(resolveResult);
		}
		else
		{
			Result top = (Result) result.peek();
			if (!top.getDepotFilename().equals(resolveResult.getDepotFilename()))
			{ 
				result.push(resolveResult);
			}
		}		
	}
	
	/** Handle infostring matching regular expression RE_WITHOUT_REVISIONS.
	  *
	  * @param re the regular expression. 
	  */
	private void handleWithoutRevisions(RE re)
	{
		Result resolveResult = new Result();
		resolveResult.setLocalFilename(re.getParen(1));
		resolveResult.setDepotFilename(re.getParen(3));
		
		if (result.isEmpty())
		{
			result.push(resolveResult);
		}
		else
		{
			Result top = (Result) result.peek();
			if (!top.getDepotFilename().equals(resolveResult.getDepotFilename()))
			{ 
				result.push(resolveResult);
			}
		}
		
	}

	// ---------------------------------------------------------------------- Private Nested Classes

	/**
	  * Implementation of the IResolveResult interface. Stores information
	  * provided by the 'p4 resolve' command. 
	  * 
	  * @see IResolveResult
	  */
	public class Result implements IResolveResult
	{
		// ----------------------------------------------------------------------- Private Variables
		
		/** Stores the depot filename. */
		private String depotFilename;
		/** Stores the filename. */
		private String filename;
		/** Stores the revision. */
		private Integer revision;
		/** Indicates that the file was skpped. */
		private boolean skipped;
		
		// -------------------------------------------------------------------------- Public Methods
		
		/** @see IResolveResult#getDepotFilename() */
		public String getDepotFilename() 
		{
			return depotFilename;
		}

		
		/** @see IResolveResult#getLocalFilename() */
		public String getLocalFilename()
		{
			return filename;
		}
		
		/** @see IResolveResult#getRevision() */
		public Integer getRevision() 
		{
			return revision;
		}
		
		/** @see IResolveResult#isSkipped() */
		public boolean isSkipped()
		{
			return skipped;
		}
		
		// --------------------------------------------------------------------------------- Methods
		
		/** Sets the depot filename.
		  *
		  * @param newDepotFilename the new depot filename. 
		  */
		void setDepotFilename(String newDepotFilename)
		{
			depotFilename = newDepotFilename;
		}
		
		/** Sets the filename.
		  * 
		  * @param filename The filename to set
		  */
		void setLocalFilename(String filename) 
		{
			this.filename = filename;
		}
		
		/** Sets the revision to resolve.
		  *
		  * @param theRevision the revision value. 
		  */		
		void setRevision(Integer theRevision)
		{
			revision = theRevision;
		}
		
		/** Sets the skipped value.
		  * 
		  * @param isSkipped true or false.
		  */
		void setIsSkipped(boolean isSkipped)
		{
			skipped = isSkipped;
		}
	}	
}
